using System;
using System.Text;
using Xunit;
using Res=CodePlex.TfsLibrary.UnitTest.Properties.Resources;

namespace CodePlex.TfsLibrary
{
    public class MergeEngineTest
    {
        static string LoadResource(byte[] bytes)
        {
            return Encoding.UTF8.GetString(bytes);
        }

        public class Adds : MergeEngineTest
        {
            [Fact]
            public void AddToLeftAndRight()
            {
                string[] original = new string[] { "a", "c", "e" };
                string[] left = new string[] { "a", "b", "c", "e" };
                string[] right = new string[] { "a", "c", "d", "e" };
                string[] expected = new string[] { "a", "b", "c", "d", "e" };

                string[] result = MergeEngine.Merge(original, left, right);

                Assert.Equal(expected, result);
            }

            [Fact]
            public void IdenticalAddsAtBottom()
            {
                string[] original = new string[] { "a", "b" };
                string[] updated = new string[] { "a", "b", "c" };

                string[] result = MergeEngine.Merge(original, updated, updated);

                Assert.Equal(updated, result);
            }

            [Fact]
            public void IdenticalAddsAtMiddle()
            {
                string[] original = new string[] { "a", "c" };
                string[] updated = new string[] { "a", "b", "c" };

                string[] result = MergeEngine.Merge(original, updated, updated);

                Assert.Equal(updated, result);
            }

            [Fact]
            public void IdenticalAddsAtTop()
            {
                string[] original = new string[] { "b", "c" };
                string[] updated = new string[] { "a", "b", "c" };

                string[] result = MergeEngine.Merge(original, updated, updated);

                Assert.Equal(updated, result);
            }

            [Fact]
            public void SingleAddOnLeft()
            {
                string[] original = new string[] { "a" };
                string[] left = new string[] { "a", "b" };

                string[] result = MergeEngine.Merge(original, left, original);

                Assert.Equal(left, result);
            }

            [Fact]
            public void SingleAddOnRight()
            {
                string[] original = new string[] { "a" };
                string[] right = new string[] { "a", "b" };

                string[] result = MergeEngine.Merge(original, original, right);

                Assert.Equal(right, result);
            }
        }

        public class ComplexMerges : MergeEngineTest
        {
            [Fact]
            public void Bug362()
            {
                string[] original = LoadResource(Res.ProjectReleasesSidebar_r423).Split(new string[] { "\r\n" }, StringSplitOptions.None);
                string[] left = LoadResource(Res.ProjectReleasesSidebar_r475).Split(new string[] { "\r\n" }, StringSplitOptions.None);
                string[] right = LoadResource(Res.ProjectReleasesSidebar_mine).Split(new string[] { "\r\n" }, StringSplitOptions.None);

                Assert.Throws<MergeConflictException>(
                    delegate
                    {
                        MergeEngine.Merge(original, left, right);
                    });
            }

            [Fact]
            public void ComplexMerge1()
            {
                string[] original = { "a", "b", "c", "d", "e", "f", "g" };
                string[] left = { "a", "ab", "b", "c", "cd1", "cd2", "d", "e", "f" };
                string[] right = { "@", "a", "b", "bc", "c", "d", "e", "new f", "g" };
                string[] expected = { "@", "a", "ab", "b", "bc", "c", "cd1", "cd2", "d", "e", "new f" };

                string[] result = MergeEngine.Merge(original, left, right);

                Assert.Equal(expected, result);
            }
        }

        public class Conflicts : MergeEngineTest
        {
            [Fact]
            public void ConflictedAdd()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] left = new string[] { "a", "b", "b1", "c" };
                string[] right = new string[] { "a", "b", "b2", "c" };

                Assert.Throws<MergeConflictException>(
                    delegate
                    {
                        MergeEngine.Merge(original, left, right);
                    });
            }

            [Fact]
            public void ConflictedModify()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] left = new string[] { "a", "x", "c" };
                string[] right = new string[] { "a", "y", "c" };

                Assert.Throws<MergeConflictException>(
                    delegate
                    {
                        MergeEngine.Merge(original, left, right);
                    });
            }

            [Fact]
            public void ConflictedMultiLineModify1()
            {
                string[] original = new string[] { "a", "b" };
                string[] left = new string[] { "@", "b" };
                string[] right = new string[] { "$", "#" };

                Assert.Throws<MergeConflictException>(
                    delegate
                    {
                        MergeEngine.Merge(original, left, right);
                    });
            }

            [Fact]
            public void ConflictedMultiLineModify2()
            {
                string[] original = new string[] { "1", "2", "3", "4", "5", "6" };
                string[] left = new string[] { "1", "2'", "3", "4", "5", "6" };
                string[] right = new string[] { "1", "2''", "3''", "4", "5", "6" };

                Assert.Throws<MergeConflictException>(
                    delegate
                    {
                        MergeEngine.Merge(original, left, right);
                    });
            }

            [Fact]
            public void ConflictedMultiLineModify3()
            {
                string[] original = new string[] { "1", "2", "3", "4", "5", "6" };
                string[] left = new string[] { "1", "2", "3'", "4", "5", "6" };
                string[] right = new string[] { "1", "2''", "3''", "4", "5", "6" };

                Assert.Throws<MergeConflictException>(
                    delegate
                    {
                        MergeEngine.Merge(original, left, right);
                    });
            }

            [Fact]
            public void ConflictedMultiLineModify4()
            {
                string[] original = new string[] { "1", "2", "3", "4", "5", "6" };
                string[] left = new string[] { "1", "2'", "6" };
                string[] right = new string[] { "1", "5''", "6" };

                Assert.Throws<MergeConflictException>(
                    delegate
                    {
                        MergeEngine.Merge(original, left, right);
                    });
            }
        }

        public class Deletes : MergeEngineTest
        {
            [Fact]
            public void DeleteFromLeftAndRight()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] left = new string[] { "a", "b" };
                string[] right = new string[] { "a", "c" };
                string[] expected = new string[] { "a" };

                string[] result = MergeEngine.Merge(original, left, right);

                Assert.Equal(expected, result);
            }

            [Fact]
            public void IdenticalDeletesFromBottom()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] updated = new string[] { "a", "b" };

                string[] result = MergeEngine.Merge(original, updated, updated);

                Assert.Equal(updated, result);
            }

            [Fact]
            public void IdenticalDeletesFromMiddle()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] updated = new string[] { "a", "c" };

                string[] result = MergeEngine.Merge(original, updated, updated);

                Assert.Equal(updated, result);
            }

            [Fact]
            public void IdenticalDeletesFromTop()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] updated = new string[] { "b", "c" };

                string[] result = MergeEngine.Merge(original, updated, updated);

                Assert.Equal(updated, result);
            }
        }

        public class Modifys : MergeEngineTest
        {
            [Fact]
            public void IdentifyModificationsOnBottom()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] updated = new string[] { "a", "b", "x" };

                string[] result = MergeEngine.Merge(original, updated, updated);

                Assert.Equal(updated, result);
            }

            [Fact]
            public void IdentifyModificationsOnMiddle()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] updated = new string[] { "a", "x", "c" };

                string[] result = MergeEngine.Merge(original, updated, updated);

                Assert.Equal(updated, result);
            }

            [Fact]
            public void IdentifyModificationsOnTop()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] updated = new string[] { "x", "b", "c" };

                string[] result = MergeEngine.Merge(original, updated, updated);

                Assert.Equal(updated, result);
            }

            [Fact]
            public void SingleModificationFromLeft()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] left = new string[] { "x", "b", "c" };

                string[] result = MergeEngine.Merge(original, left, original);

                Assert.Equal(left, result);
            }

            [Fact]
            public void SingleModificationFromRight()
            {
                string[] original = new string[] { "a", "b", "c" };
                string[] right = new string[] { "x", "b", "c" };

                string[] result = MergeEngine.Merge(original, original, right);

                Assert.Equal(right, result);
            }
        }

        public class Same : MergeEngineTest
        {
            [Fact]
            public void NoChangesOnEitherSide()
            {
                string[] original = new string[] { "a", "b" };

                string[] result = MergeEngine.Merge(original, original, original);

                Assert.Equal(original, result);
            }
        }
    }
}